__modify_IO_APIC_irq(irq, 0, 0x00010000);
}
-/* trigger = 0 */
-static void __edge_IO_APIC_irq (unsigned int irq)
+/* mask = 1, trigger = 0 */
+static void __mask_and_edge_IO_APIC_irq (unsigned int irq)
{
- __modify_IO_APIC_irq(irq, 0, 0x00008000);
+ __modify_IO_APIC_irq(irq, 0x00010000, 0x00008000);
}
-/* trigger = 1 */
-static void __level_IO_APIC_irq (unsigned int irq)
+/* mask = 0, trigger = 1 */
+static void __unmask_and_level_IO_APIC_irq (unsigned int irq)
{
- __modify_IO_APIC_irq(irq, 0x00008000, 0);
+ __modify_IO_APIC_irq(irq, 0x00008000, 0x00010000);
}
static void mask_IO_APIC_irq (unsigned int irq)
}
static void mask_and_ack_level_ioapic_irq (unsigned int irq)
+{
+}
+
+static void end_level_ioapic_irq (unsigned int irq)
{
unsigned long v;
int i;
- mask_IO_APIC_irq(irq);
/*
* It appears there is an erratum which affects at least version 0x11
* of I/O APIC (that's the 82093AA and cores integrated into various
if (!(v & (1 << (i & 0x1f)))) {
atomic_inc(&irq_mis_count);
spin_lock(&ioapic_lock);
- __edge_IO_APIC_irq(irq);
- __level_IO_APIC_irq(irq);
+ __mask_and_edge_IO_APIC_irq(irq);
+ __unmask_and_level_IO_APIC_irq(irq);
spin_unlock(&ioapic_lock);
}
}
-static void end_level_ioapic_irq (unsigned int irq)
-{
- unmask_IO_APIC_irq(irq);
-}
-
static unsigned int startup_edge_ioapic_vector(unsigned int vector)
{
int irq = vector_to_irq(vector);
u8 nr_guests;
u8 in_flight;
u8 shareable;
+ u8 ack_type;
+#define ACKTYPE_NONE 0 /* Final ACK is not required */
+#define ACKTYPE_SINGLE 1 /* Final ACK on any CPU */
+#define ACKTYPE_MULTI 2 /* Final ACK on the CPU that was interrupted */
+ cpumask_t cpu_ack_map;
struct domain *guest[IRQ_MAX_GUESTS];
} irq_guest_action_t;
struct domain *d;
int i;
+ if ( unlikely(action->nr_guests == 0) )
+ {
+ /* An interrupt may slip through while freeing an ACKTYPE_MULTI irq. */
+ ASSERT(action->ack_type == ACKTYPE_MULTI);
+ desc->handler->end(vector);
+ return;
+ }
+
+ if ( action->ack_type == ACKTYPE_MULTI )
+ cpu_set(smp_processor_id(), action->cpu_ack_map);
+
for ( i = 0; i < action->nr_guests; i++ )
{
d = action->guest[i];
- if ( !test_and_set_bit(irq, &d->pirq_mask) )
+ if ( (action->ack_type != ACKTYPE_NONE) &&
+ !test_and_set_bit(irq, &d->pirq_mask) )
action->in_flight++;
send_guest_pirq(d, irq);
}
}
+static void end_guest_irq(void *data)
+{
+ irq_desc_t *desc = data;
+ irq_guest_action_t *action = (irq_guest_action_t *)desc->action;
+ unsigned long flags;
+
+ spin_lock_irqsave(&desc->lock, flags);
+ if ( (desc->status & IRQ_GUEST) &&
+ (action->in_flight == 0) &&
+ test_and_clear_bit(smp_processor_id(), &action->cpu_ack_map) )
+ desc->handler->end(desc - irq_desc);
+ spin_unlock_irqrestore(&desc->lock, flags);
+}
+
int pirq_guest_unmask(struct domain *d)
{
- irq_desc_t *desc;
- unsigned int pirq;
- shared_info_t *s = d->shared_info;
+ irq_desc_t *desc;
+ irq_guest_action_t *action;
+ cpumask_t cpu_ack_map = CPU_MASK_NONE;
+ unsigned int pirq, cpu = smp_processor_id();
+ shared_info_t *s = d->shared_info;
for ( pirq = find_first_bit(d->pirq_mask, NR_PIRQS);
pirq < NR_PIRQS;
pirq = find_next_bit(d->pirq_mask, NR_PIRQS, pirq+1) )
{
- desc = &irq_desc[irq_to_vector(pirq)];
+ desc = &irq_desc[irq_to_vector(pirq)];
+ action = (irq_guest_action_t *)desc->action;
+
spin_lock_irq(&desc->lock);
if ( !test_bit(d->pirq_to_evtchn[pirq], &s->evtchn_mask[0]) &&
- test_and_clear_bit(pirq, &d->pirq_mask) &&
- (--((irq_guest_action_t *)desc->action)->in_flight == 0) )
- desc->handler->end(irq_to_vector(pirq));
+ test_and_clear_bit(pirq, &d->pirq_mask) )
+ {
+ ASSERT(action->ack_type != ACKTYPE_NONE);
+ if ( --action->in_flight == 0 )
+ {
+ if ( (action->ack_type == ACKTYPE_SINGLE) ||
+ test_and_clear_bit(cpu, &action->cpu_ack_map) )
+ desc->handler->end(irq_to_vector(pirq));
+ cpu_ack_map = action->cpu_ack_map;
+ }
+ }
spin_unlock_irq(&desc->lock);
+
+ if ( !cpus_empty(cpu_ack_map) )
+ {
+ on_selected_cpus(cpu_ack_map, end_guest_irq, desc, 1, 0);
+ cpu_ack_map = CPU_MASK_NONE;
+ }
}
return 0;
}
+int pirq_acktype(int irq)
+{
+ irq_desc_t *desc;
+ unsigned int vector;
+
+ vector = irq_to_vector(irq);
+ if ( vector == 0 )
+ return ACKTYPE_NONE;
+
+ desc = &irq_desc[vector];
+
+ /*
+ * Edge-triggered IO-APIC interrupts need no final acknowledgement:
+ * we ACK early during interrupt processing.
+ */
+ if ( !strcmp(desc->handler->typename, "IO-APIC-edge") )
+ return ACKTYPE_NONE;
+
+ /* Legacy PIC interrupts can be acknowledged from any CPU. */
+ if ( !strcmp(desc->handler->typename, "XT-PIC") )
+ return ACKTYPE_SINGLE;
+
+ /*
+ * By default assume that an interrupt must be finally acknowledged on
+ * the CPU on which it was received. This is true for level-triggered
+ * IO-APIC interrupts, for example, where we tickle the LAPIC to EOI.
+ */
+ return ACKTYPE_MULTI;
+}
+
int pirq_guest_bind(struct vcpu *v, int irq, int will_share)
{
unsigned int vector;
goto out;
}
- action->nr_guests = 0;
- action->in_flight = 0;
- action->shareable = will_share;
-
+ action->nr_guests = 0;
+ action->in_flight = 0;
+ action->shareable = will_share;
+ action->ack_type = pirq_acktype(irq);
+ action->cpu_ack_map = CPU_MASK_NONE;
+
desc->depth = 0;
desc->status |= IRQ_GUEST;
desc->status &= ~IRQ_DISABLED;
unsigned int vector = irq_to_vector(irq);
irq_desc_t *desc = &irq_desc[vector];
irq_guest_action_t *action;
+ cpumask_t cpu_ack_map;
unsigned long flags;
int i;
action = (irq_guest_action_t *)desc->action;
- if ( test_and_clear_bit(irq, &d->pirq_mask) &&
- (--action->in_flight == 0) )
- desc->handler->end(vector);
+ i = 0;
+ while ( action->guest[i] && (action->guest[i] != d) )
+ i++;
+ memmove(&action->guest[i], &action->guest[i+1], IRQ_MAX_GUESTS-i-1);
+ action->nr_guests--;
- if ( action->nr_guests == 1 )
- {
- desc->action = NULL;
- xfree(action);
- desc->depth = 1;
- desc->status |= IRQ_DISABLED;
- desc->status &= ~IRQ_GUEST;
- desc->handler->shutdown(vector);
- }
- else
+ switch ( action->ack_type )
{
- i = 0;
- while ( action->guest[i] && (action->guest[i] != d) )
- i++;
- memmove(&action->guest[i], &action->guest[i+1], IRQ_MAX_GUESTS-i-1);
- action->nr_guests--;
+ case ACKTYPE_SINGLE:
+ if ( test_and_clear_bit(irq, &d->pirq_mask) &&
+ (--action->in_flight == 0) )
+ desc->handler->end(vector);
+ break;
+ case ACKTYPE_MULTI:
+ if ( test_and_clear_bit(irq, &d->pirq_mask) )
+ --action->in_flight;
+ while ( action->in_flight == 0 )
+ {
+ /* We cannot release guest info until all pending ACKs are done. */
+ cpu_ack_map = action->cpu_ack_map;
+ if ( cpus_empty(cpu_ack_map) )
+ break;
+
+ /* We cannot hold the lock while interrupting other CPUs. */
+ spin_unlock_irqrestore(&desc->lock, flags);
+ on_selected_cpus(cpu_ack_map, end_guest_irq, desc, 1, 1);
+ spin_lock_irqsave(&desc->lock, flags);
+
+ /* The world can change while we do not hold the lock. */
+ if ( !(desc->status & IRQ_GUEST) )
+ goto out;
+ if ( (action->ack_type != ACKTYPE_MULTI) ||
+ (action->nr_guests != 0) )
+ break;
+ }
+ break;
}
+ BUG_ON(test_bit(irq, &d->pirq_mask));
+
+ if ( action->nr_guests != 0 )
+ goto out;
+
+ BUG_ON(action->in_flight != 0);
+ BUG_ON(!cpus_empty(action->cpu_ack_map));
+
+ desc->action = NULL;
+ xfree(action);
+ desc->depth = 1;
+ desc->status |= IRQ_DISABLED;
+ desc->status &= ~IRQ_GUEST;
+ desc->handler->shutdown(vector);
+
+ out:
spin_unlock_irqrestore(&desc->lock, flags);
return 0;
}